Skip to content

S11-12 Vue-项目:mr-vue3-ts-consult-patient2

[TOC]

Consult

image-20250312175823339

image-20250312175839658

image-20250312180136818

TS类型

知识点Enum

1、使用枚举类型定义问诊类型、问诊时间

ts
// 问诊类型
export enum ConsultType {
  /** 找医生 */
  Doctor = 1,
  /** 快速问诊 */
  Fast = 2,
  /** 开药问诊 */
  Medication = 3
}
// 问诊时间,以1自增可以省略
export enum IllnessTime {
  /** 一周内 */
  Week = 1,
  /** 一月内 */
  Month,
  /** 半年内 */
  HalfYear,
  /** 半年以上 */
  More
}

2、定义问诊记录类型

ts
import { ConsultType, IllnessTime } from '@/enums'

// 图片列表
export type Image = {
  /** 图片ID */
  id: string
  /** 图片地址 */
  url: string
}
// 问诊记录
export type Consult = {
  /** 问诊记录ID */
  id: string
  /** 问诊类型 */
  type: ConsultType
  /** 快速问诊类型,0 普通 1 三甲 */
  illnessType: 0 | 1
  /** 科室ID */
  depId: string
  /** 疾病描述 */
  illnessDesc: string
  /** 疾病持续时间 */
  illnessTime: IllnessTime
  /** 是否就诊过,0 未就诊过  1 就诊过 */
  consultFlag: 0 | 1
  /** 图片数组 */
  pictures: Image[]
  /** 患者ID */
  patientId: string
  /** 优惠券ID */
  couponId: string
}

// 问诊记录-全部可选
export type PartialConsult = Partial<Consult>
// Required 转换为全部必须   Partial 转换问全部可选  两个内置的泛型类型

问诊类型

image-20250312214932564

定义问诊仓库

1、在 store/consult.ts 中定义 consultStore

image-20250312215553677

2、在 store/index.ts 中统一导出consultStore

image-20250312215622172

记录问诊类型

1、在 store/consult.ts 中定义 setType() 方法,记录问诊类型

image-20250312215907293

2、在 home 组件调用 setType() 方法,记录问诊类型

image-20250312220022460

问诊级别:consult-fast

image-20250312220737249

image-20250312220153218

路由规则

image-20250312220447274

页面布局

1、HTML

html
<template>
  <div class="consult-fast-page">
    <cp-nav-bar title="极速问诊" right-text="问诊记录"></cp-nav-bar>
    <div class="fast-logo">
      <img class="img" src="@/assets/consult-fast.png" alt="" />
      <p class="text"><span>20s</span> 快速匹配专业医生</p>
    </div>
    <div class="fast-type">
      <router-link to="/consult/dep" class="item">
        <cp-icon class="pic" name="consult-doctor"></cp-icon>
        <div class="info">
          <p>三甲图文问诊</p>
          <p>三甲主治及以上级别医生</p>
        </div>
        <van-icon name="arrow"></van-icon>
      </router-link>
      <router-link to="/consult/dep" class="item">
        <cp-icon class="pic" name="consult-message"></cp-icon>
        <div class="info">
          <p>普通图文问诊</p>
          <p>二甲主治及以上级别医生</p>
        </div>
        <van-icon name="arrow"></van-icon>
      </router-link>
    </div>
  </div>
</template>

2、样式

less
.consult-fast-page {
  padding-top: 46px;
  .fast-logo {
    padding: 30px 0;
    text-align: center;
    .img {
      width: 240px;
    }
    .text {
      font-size: 16px;
      margin-top: 10px;
      > span {
        color: var(--cp-primary);
      }
    }
  }
  .fast-type {
    padding: 15px;
    .item {
      display: flex;
      padding: 16px;
      border-radius: 4px;
      align-items: center;
      margin-bottom: 16px;
      border: 0.5px solid var(--cp-line);
    }
    .pic {
      width: 40px;
      height: 40px;
    }
    .info {
      margin-left: 12px;
      flex: 1;
      > p:first-child {
        font-size: 16px;
        color: var(--cp-text1);
        margin-bottom: 4px;
      }
      > p:last-child {
        font-size: 13px;
        color: var(--cp-tag);
      }
    }
    .van-icon {
      color: var(--cp-tip);
    }
  }
}

记录问诊级别

1、在 store/consult.ts 中定义 setIllnessType() 方法,记录问诊级别

image-20250312221018269

2、在 consult-fast 组件调用 setIllnessType() 方法,记录问诊级别

image-20250312221136750

问诊科室:consult-department

image-20250312221241332

image-20250312221435862

路由规则

image-20250312221700726

页面布局

1、HTML

html
<template>
  <div class="consult-dep-page">
    <!-- 导航栏 -->
    <cp-nav-bar title="选择科室" />
    <div class="wrapper">
      <!-- 一级科室 -->
      <van-sidebar v-model="active">
        <van-sidebar-item title="内科" />
        <van-sidebar-item title="外科" />
        <van-sidebar-item title="皮肤科" />
        <van-sidebar-item title="骨科" />
      </van-sidebar>
      <!-- 二级科室 -->
      <div class="sub-dep">
        <router-link to="/consult/illness">科室一</router-link>
        <router-link to="/consult/illness">科室二</router-link>
        <router-link to="/consult/illness">科室三</router-link>
      </div>
    </div>
  </div>
</template>

2、样式

less
.van-sidebar {
  width: 114px;
  &-item {
    padding: 14px;
    color: var(--cp-tag);
    &--select {
      color: var(--cp-main);
      font-weight: normal;
      &::before {
        display: none;
      }
    }
  }
}
.consult-dep-page {
  padding-top: 46px;
  .wrapper {
    height: calc(100vh - 46px);
    overflow: hidden;
    display: flex;
    .sub-dep {
      flex: 1;
      height: 100%;
      overflow-y: auto;
      > a {
        display: block;
        padding: 14px 30px;
        color: var(--cp-dark);
      }
    }
  }
}

渲染请求数据

TS类型
ts
// 二级科室
export type SubDep = {
  /** 科室ID */
  id: string
  /** 科室名称 */
  name: string
}

// 一级科室
export type TopDep = SubDep & {
  /** 二级科室数组 */
  child: SubDep[]
}
接口
  • URL/dep/all

  • 类型GET

  • token:携带

  • 参数:无

  • 返回数据

    image-20250312222322921

渲染数据

1、在 services/consult.ts 中发送网络请求

image-20250312222744813

2、在组件中调用请求方法,获取一级科室列表数据

image-20250312222912309

3、渲染一级科室数据

image-20250312223334505

4、根据 active 属性,获取二级科室列表数据

image-20250312223256613

5、渲染二级科室数据

image-20250312223418582

记录问诊科室

1、在 store/consult.ts 中定义 setDep() 方法,记录问诊科室

image-20250312223619113

2、在 consult-department 组件调用 setDep() 方法,记录问诊科室

image-20250312223857452

病情描述:consult-illness

image-20250313085619022

image-20250313085928993

路由规则

image-20250313090034901

页面布局

1、HTML

html
<template>
  <div class="consult-illness-page">
    <cp-nav-bar title="图文问诊" />
    <!-- 医生提示 -->
    <div class="illness-tip van-hairline--bottom">
      <img class="img" src="@/assets/avatar-doctor.svg" />
      <div class="info">
        <p class="tit">在线医生</p>
        <p class="tip">
          请描述你的疾病或症状、是否用药、就诊经历,需要我听过什么样的帮助
        </p>
        <p class="safe">
          <cp-icon name="consult-safe" /><span>内容仅医生可见</span>
        </p>
      </div>
    </div>
    <!-- 收集信息 -->
    <div class="illness-form">
      <van-field
        type="textarea"
        rows="3"
        placeholder="请详细描述您的病情,病情描述不能为空"
      ></van-field>
      <div class="item">
        <p>本次患病多久了?</p>
      </div>
      <div class="item">
        <p>此次病情是否去医院就诊过?</p>
      </div>
    </div>
  </div>
</template>

2、样式

less
.consult-illness-page {
  padding-top: 46px;
}
.illness-tip {
  display: flex;
  padding: 15px;
  .img {
    width: 52px;
    height: 52px;
    border-radius: 4px;
    overflow: hidden;
    margin-top: 10px;
  }
  .info {
    flex: 1;
    padding-left: 12px;
    .tit {
      font-size: 16px;
      margin-bottom: 5px;
    }
    .tip {
      padding: 12px;
      background: var(--cp-bg);
      color: var(--cp-text3);
      font-size: 13px;
      margin-bottom: 10px;
    }
    .safe {
      font-size: 10px;
      color: var(--cp-text3);
      display: flex;
      align-items: center;
      .cp-icon {
        font-size: 12px;
        margin-right: 2px;
      }
    }
  }
}
.illness-form {
  padding: 15px;
  .van-field {
    padding: 0;
    &::after {
      border-bottom: none;
    }
  }
  .item {
    > p {
      color: var(--cp-text3);
      padding: 15px 0;
    }
  }
}

表单数据

1、TS类型

ts
export type ConsultIllness = Pick<
  PartialConsult,
  'illnessDesc' | 'illnessTime' | 'consultFlag' | 'pictures'
>

2、表单数据

ts
import type { ConsultIllness } from '@/types/consult'
import { ref } from 'vue'
import { IllnessTime } from '@/enums'

const timeOptions = [
  { label: '一周内', value: IllnessTime.Week },
  { label: '一月内', value: IllnessTime.Month },
  { label: '半年内', value: IllnessTime.HalfYear },
  { label: '半年以上', value: IllnessTime.More }
]
const flagOptions = [
  { label: '就诊过', value: 1 },
  { label: '没就诊过', value: 0 }
]
const form = ref<ConsultIllness>({
  illnessDesc: '',
  illnessTime: undefined,
  consultFlag: undefined,
  pictures: []
})

渲染数据

image-20250313090807790

上传图片@

image-20250313090923115

image-20250313091300966

页面布局

1、HTML

html
      <div class="illness-img">
        <van-uploader></van-uploader>
        <p class="tip" >上传内容仅医生可见,最多9张图,最大5MB</p>
      </div>

2、样式

less
.illness-img {
  padding-top: 16px;
  margin-bottom: 40px;
  display: flex;
  align-items: center;
  .tip {
    font-size: 12px;
    color: var(--cp-tip);
  }
  ::v-deep() {
    .van-uploader {
      &__preview {
        &-delete {
          left: -6px;
          top: -6px;
          border-radius: 50%;
          background-color: var(--cp-primary);
          width: 20px;
          height: 20px;
          &-icon {
            transform: scale(0.9) translate(-22%, 22%);
          }
        }
        &-image {
          border-radius: 8px;
          overflow: hidden;
        }
      }
      &__upload {
        border-radius: 8px;
      }
      &__upload-icon {
        color: var(--cp-text3);
      }
    }
  }
}
配置图片文字

image-20250313091420287

限制数量和大小

image-20250313091520456

收集图片数据

image-20250313091817932

实现上传业务

image-20250313092316157

接口
  • URL/upload

  • 类型POST

  • token:携带

  • 参数

    ts
    {
      file: FormData // 上传的文件
    }
  • 返回数据

    image-20250313092507137

上传图片

1、在 services/consult.ts 中发送网络请求

image-20250313092910288

2、在组件中,通过after-read绑定图片上传完毕后的回调函数

image-20250313093040343

image-20250313093249530

3、通过after-read的item.status参数定义上传状态

image-20250313093737007

4、同步上传数据到form.pictures中

image-20250313094159109

3、在组件中,通过@delete监听删除文件预览的处理函数

image-20250313094548561

记录病情描述

image-20250313094818625

按钮激活

1、默认情况下按钮有disabled类,此时按钮为灰色状态

image-20250313095359180

2、样式默认为灰色

less
.van-button {
  font-size: 16px;
  margin-bottom: 30px;
  &.disabled {
    opacity: 1;
    background: #fafafa;
    color: #d9dbde;
    border: #fafafa;
  }
}

3、激活:当病情描述、时间、是否就诊选中时激活按钮

image-20250313100000655

image-20250313100027123

轻提示校验

image-20250313100255265

image-20250313100308618

记录病情描述

1、在 store/consult.ts 中定义记录病情描述的方法 setIllness()

image-20250313100559367

2、在组件中调用 setIllness() 方法,记录病情描述,并跳转到选择患者页面

image-20250313100844841

回显数据

image-20250313101204359

image-20250313101251327

1、当进入到病情描述页面时,判断是否已经存在病情描述数据,如果存在则回显该数据。

image-20250313101728108

2、通过设置closeOnPopstate: false处理从选择患者页面回退时,无法弹出弹框的问题

image-20250313102133254

选择患者:patient

image-20250313102340081

image-20250313102343482

界面兼容

依据isChange=1区分选择患者、家庭档案

image-20250313102952407

选中患者效果

1、在患者项上绑定点击事件,设置选中患者id

image-20250313103620662

image-20250313103447795

2、阻止点击编辑按钮时的事件冒泡

image-20250313103551153

默认选中

思路:进入页面后,如果有默认患者,则选中该患者;如果没有默认患者,则选中第一个患者。

image-20250313104048231

记录患者ID

1、在 store/consult.ts 中,添加记录患者ID的方法 setPatient()

image-20250313104154482

2、在组件中点击下一步时,调用 setPatient() 方法记录患者ID

image-20250313104235521

image-20250313104414499

问诊支付:consult-pay

image-20250313104552040

image-20250313114916442

路由规则

image-20250313115132503

页面布局

1、HTML

html
<template>
  <div class="consult-pay-page">
    <cp-nav-bar title="支付" />
    <div class="pay-info">
      <p class="tit">图文问诊 49 元</p>
      <img class="img" src="@/assets/avatar-doctor.svg" />
      <p class="desc">
        <span>极速问诊</span>
        <span>自动分配医生</span>
      </p>
    </div>
    <van-cell-group>
      <van-cell title="优惠券" value="-¥10.00" />
      <van-cell title="积分抵扣" value="-¥10.00" />
      <van-cell title="实付款" value="¥29.00" class="pay-price" />
    </van-cell-group>
    <div class="pay-space"></div>
    <van-cell-group>
      <van-cell title="患者信息" value="李富贵 | 男 | 30岁"></van-cell>
      <van-cell title="病情描述" label="头痛,头晕,恶心"></van-cell>
    </van-cell-group>
    <div class="pay-schema">
      <van-checkbox>我已同意 <span class="text">支付协议</span></van-checkbox>
    </div>
    <van-submit-bar button-type="primary" :price="2900" button-text="立即支付" text-align="left" />
  </div>
</template>

2、样式

less
.consult-pay-page {
  padding: 46px 0 50px 0;
}
.pay-info {
  display: flex;
  padding: 15px;
  flex-wrap: wrap;
  align-items: center;
  .tit {
    width: 100%;
    font-size: 16px;
    margin-bottom: 10px;
  }
  .img {
    margin-right: 10px;
    width: 38px;
    height: 38px;
    border-radius: 4px;
    overflow: hidden;
  }
  .desc {
    flex: 1;
    > span {
      display: block;
      color: var(--cp-tag);
      &:first-child {
        font-size: 16px;
        color: var(--cp-text2);
      }
    }
  }
}
.pay-price {
  ::v-deep() {
    .vam-cell__title {
      font-size: 16px;
    }
    .van-cell__value {
      font-size: 16px;
      color: var(--cp-price);
    }
  }
}
.pay-space {
  height: 12px;
  background-color: var(--cp-bg);
}
.pay-schema {
  height: 56px;
  display: flex;
  align-items: center;
  justify-content: center;
  .text {
    color: var(--cp-primary);
  }
}
::v-deep() {
  .van-submit-bar__button {
    font-weight: normal;
    width: 160px;
  }
}
.pay-type {
  .amount {
    padding: 20px;
    text-align: center;
    font-size: 16px;
    font-weight: bold;
  }
  .btn {
    padding: 15px;
  }
  .van-cell {
    align-items: center;
    .cp-icon {
      margin-right: 10px;
      font-size: 18px;
    }
    .van-checkbox :deep(.van-checkbox__icon) {
      font-size: 16px;
    }
  }
}

渲染请求数据

TS类型
ts
// 问诊订单预支付传参
export type ConsultOrderPreParams = Pick<PartialConsult, 'type' | 'illnessType'>

// 问诊订单预支付信息
export type ConsultOrderPreData = {
  /** 积分抵扣 */
  pointDeduction: number
  /** 优惠券抵扣 */
  couponDeduction: number
  /** 优惠券ID */
  couponId: string
  /** 需付款 */
  payment: number
  /** 实付款 */
  actualPayment: number
}
接口-预支付信息
  • URL/patient/consult/order/pre

  • 类型GET

  • token:携带

  • 参数

    ts
    {
      // 急速问诊只需要以下2个属性
      type: string // 问诊类型:1 找医生,2 极速问诊,3 开药问诊
      illnessType: string // 极速问诊级别:0 普通,1 三甲
      
      useCoupon: string // 是否使用优惠券:0 不使用优惠券,1 使用优惠券(默认,且按最大优惠券使用)。
      couponId: string // 可使用优惠券id
      docId: string // 医生id,当type为1时必传
    }
  • 返回数据

    image-20250313120004351

接口-患者信息
  • URL/patient/info/:id

  • 类型GET

  • token:携带

  • 参数

    ts
    {
      id: string // 患者id
    }
  • 返回数据

    image-20250313120729659

渲染数据

1、在 services/consult.ts 中发送网络请求

image-20250313120413162

2、在 services/patient.ts 中发送网络请求

image-20250313120820160

3、在组件中,调用请求方法

image-20250313121148430

image-20250313121339279

4、渲染数据

image-20250313122347297

记录优惠券

1、在store中定义 setCoupon() 方法记录优惠券

image-20250313122540959

2、在组件中,获取到预支付信息后,调用 setCoupon() 方法记录优惠券

image-20250313122648833

骨架屏@

image-20250313122956324

image-20250313123012490

实现支付

支付流程

image-20250313123335722

image-20250313123346032

生成订单

image-20250313123903246

支付抽屉面板

1、在 van-submit-bar 上绑定点击事件,打开支付抽屉面板

image-20250313124336693

image-20250313124351991

2、支付抽屉面板布局

image-20250313124520146

接口
  • URL/patient/consult/order

  • 类型POST

  • token:携带

  • 参数

    ts
    Consult
  • 返回数据

    image-20250313125448132

实现生成订单

1、选择支付方式

image-20250313125226217

2、在 services/consult.ts 中发送网络请求

image-20250313125656157

3、在组件中调用请求方法,获取订单id,同时添加loading效果

image-20250313130020374

image-20250313130324668

清理问诊数据

1、在store中定义 clear() 方法,清理问诊数据

image-20250313130118592

2、在组件中调用 clear() 方法,清理问诊数据

image-20250313130242618

用户引导

image-20250313130714618

阻止返回上页

image-20250313131447145

阻止关闭抽屉

1、设置属性阻止关闭抽屉

image-20250313131152274

2、监听用户点击灰色区域关闭抽屉,弹出确认弹框

image-20250313131341250

image-20250313131736289

刷新页面提示

image-20250313144610831

思路:刷新页面后在onMounted钩子中校验所有需要的key对应的值是否为undefined,如果所有值都不是undefined,则校验通过,否则弹出提示框,提示数据不完整。

image-20250313144702121

实现支付
接口
  • URL/patient/consult/pay

  • 类型POST

  • token:携带

  • 参数

    ts
    {
      paymentMethod: string // 支付方式:0 微信支付,1 支付宝,2 云闪付
      orderId: string // 订单id
      payCallback: string // 回跳地址,http://域名/回跳页面
    }
  • 返回数据

    image-20250313124951980

实现支付

1、在 services/consult.ts 中发送网络请求

image-20250313145554602

2、在组件中调用请求方法,获取支付地址

image-20250313145638533

image-20250313150102211

处理支付失败

思路:支付完成后等待几秒会跳转到指定地址,url参数中包括支付结果payResult,支付成功payResult为true,支付失败payResult为false。

在room路由的beforeEnter路由钩子中处理支付失败的跳转

image-20250313150816503

Room

路由规则

image-20250313150703607